/*
 * www.7yangche.com Inc.
 * Copyright (c) 2012 All Rights Reserved.
 */
package cbh.tool.mybatis.crud;

import cbh.tool.common.Maps;
import cbh.tool.mybatis.page.Page;
import cbh.tool.mybatis.page.PageDefault;

import java.util.List;
import java.util.Map;


/**
 * 提供一个命名空间的概念。根据该命名空间能够做到对 statement 的定位。<br>
 * 
 * 默认采用类全名作为命名空间，也可以通过创建时传入映射管理做类名管理。<br>
 * 
 * 提供默认的 INSERT/UPDATE/DELETE/SELECT/COUNT的statement， 也可以手工传入。<br>
 * 
 * @author cyzhang
 *
 */
@CrudComponent
public abstract class CrudSupport extends DbSupport {

    protected static final String DEFAULT_INSERT_STATEMENT = "insert";

    protected static final String DEFAULT_UPDATE_STATEMENT = "update";

    protected static final String DEFAULT_DELETE_STATEMENT = "delete";

    protected static final String DEFAULT_SELECT_STATEMENT = "select";

    protected static final String DEFAULT_COUNT_STATEMENT = "count";

    private String namespace;

    /////////////////////////////////////////////////////////////////////////////////
    protected <T> List<T> querySelect(String statement, Object parameter) {
        return getSqlSession().selectList(full_(statement), parameter);
    }

    protected <T> T one_(String statement) {
        return getSqlSession().selectOne(full_(statement));
    }

    protected <T> T queryOne(String statement) {
        return getSqlSession().selectOne(full_(statement));
    }

    protected <T> T queryOne(String statement, Object parameter) {
        return getSqlSession().selectOne(full_(statement), parameter);
    }

    protected void queryInsert(String statement, Object parameter) {
        getSqlSession().insert(full_(statement), parameter);
    }
    
    protected int queryUpdate(String statement, Object parameter) {
        return getSqlSession().update(full_(statement), parameter);
    }

    protected int queryDelete(String statement, Object parameter) {
        return getSqlSession().delete(full_(statement), parameter);
    }

    /////////////////////////////////////////////////////////////////////////////////
    public <T> T one_(Object parameter) {
        return this.queryOne(DEFAULT_SELECT_STATEMENT, parameter);
    }

    protected <T> List<T> select_(Object parameter) {
        return this.querySelect(DEFAULT_SELECT_STATEMENT, parameter);
    }

    protected <T> T insert_(T t) {
        this.queryInsert(DEFAULT_INSERT_STATEMENT, t);
        return t;
    }

    protected <T> int update_(T t) {
        return this.queryUpdate(DEFAULT_UPDATE_STATEMENT, t);
    }

    protected <T> int delete_(T t) {
        return this.queryDelete(DEFAULT_DELETE_STATEMENT, t);
    }

    protected Long count_(Object parameter) {
        return this.queryCount(DEFAULT_COUNT_STATEMENT, parameter);
    }

    protected Long queryCount(String statement, Object parameter) {
        return this.queryOne(statement, parameter);
    }

    protected <T> Page<T> page_(int pageNum, int pageSize) {
        return this.page_(pageNum, pageSize, null);
    }

    protected <T> Page<T> page_(int pageNum, int pageSize, Map<String, ? extends Object> parameter) {
        Object pageParameters = combine(pageNum, pageSize, parameter);
        return new PageDefault<T>(pageNum, pageSize, this.<T> select_(pageParameters), this
            .count_(parameter));
    }

    // 将参数进行组合。
    private Map<String, ? extends Object> combine(int pageNum, int pageSize,
        Map<String, ? extends Object> params) {
        pageNum = (pageNum < 1) ? 1 : pageNum;
        pageSize = (pageNum < 0) ? 0 : pageSize;
        Integer start = (pageNum - 1) * pageSize;
        Integer limit = pageSize;
        Map<String, Object> result = Maps.<String, Object> as("start", start, "limit", limit);
        if (params != null) {
            assertParamsIncludeStartOrLimit(params);
            result.putAll(params);
        }
        return result;
    }

    public void assertParamsIncludeStartOrLimit(Map<String, ? extends Object> params) {
        if (params.containsKey("start") || params.containsKey("limit")) {
            throw new IllegalArgumentException(
                "params can not be include start or limit when use page!");
        }
    }

    private String full_(String statement) {
        return String.join(this.namespace_(), ".", statement);
    }

    private String namespace_() {
        Class<?> type = this.getClass();
        if (this.namespace == null) {
            if (type.getAnnotation(Namespace.class) != null) {
                this.namespace = type.getAnnotation(Namespace.class).value();
            }
            if (type.getAnnotation(NamespaceClass.class) != null) {
                this.namespace = type.getAnnotation(NamespaceClass.class).value().getName();
            }
        }
        return this.namespace;
    }
}
